import java.io.*;
import java.text.*;

import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.NoSuchElementException;

import org.omg.CORBA.Any;
import TSpace.*;

public class ApplicationClient
{
	private static Client myClient1 = null;
	private static Client myClient2 = null;

	public static void checkCommunications(String[] args)
	{
		if (myClient1 == null)
		{
			System.out.println("(re)Attaching to TupleSpace1...");

			try
			{
				myClient1 = new Client(args[0], args[1], 1);

            // run a ping..
            myClient1.ping();

				System.out.println("(re)Attached to TupleSpace1!");
			}
			catch (Exception e)
			{
				System.out.println("Unable to attach to TupleSpace1!");
				myClient1 = null;
			}
		}
		
		if (myClient2 == null)
		{
			System.out.println("(re)Attaching to TupleSpace2...");

			try
			{
				myClient2 = new Client(args[0], args[1], 2);

            // run a ping..
            myClient2.ping();

				System.out.println("(re)Attached to TupleSpace2!");
			}
			catch (Exception e)
			{
				System.out.println("Unable to attach to TupleSpace2!");
				myClient2 = null;
			}
		}
	}

	public static TupleType typeOrNull(String tupleType)
	{
		if (tupleType == null)
			return TupleType.NULL;
			
		return TupleTypeMap.stringToTupleType(tupleType);
	}
	
	public static void main(String[] args)
	{
		if (args.length != 2)
			throw new RuntimeException("Invalid number of arguments");
		
		InputStreamReader converter = new InputStreamReader(System.in);
		BufferedReader		in_buffer = new BufferedReader(converter);

		// pegar ateh 3 valores...
		String REGEX = "(write|read|take)<(([^,:]+)(:([^:,]+))?)?(,(([^,:]+)(:([^:,]+))?))?(,(([^,:]+)(:([^:,]+))?))?>";

		Pattern p = Pattern.compile(REGEX);

		ApplicationClient.checkCommunications(args);

		if (myClient1 == null && myClient2 == null)
			throw new RuntimeException("Failed to connect, exiting...");

		while (true)
		{
			try
			{
				System.out.println("**** ");
				System.out.print("**** Command: ");

				String msg = in_buffer.readLine();

				System.out.println("****");

				Matcher m = p.matcher(msg); // get a matcher object
				m.find();

				String tupleType1 = null;
				String tupleType2 = null;
				String tupleType3 = null;

				String tupleValue1 = new String();
				String tupleValue2 = new String();
				String tupleValue3 = new String();

				if (m.group(3) != null)
				{
					if (TupleTypeMap.validateString(m.group(3)))
					{
						tupleType1 = m.group(3);
						
						if (m.group(5) != null)
							tupleValue1 = m.group(5);
					}
					else
							throw new RuntimeException("Invalid type: " + m.group(3));

					if (m.group(8) != null)
					{
						if (TupleTypeMap.validateString(m.group(8)))
						{
							tupleType2 = m.group(8);
							
							if (m.group(10) != null)
								tupleValue2 = m.group(10);
						}
						else
							throw new RuntimeException("Invalid type: " + m.group(8));

						if (m.group(13) != null)
						{
							if (TupleTypeMap.validateString(m.group(13)))
							{
								tupleType3 = m.group(13);
								
								if (m.group(15) != null)
									tupleValue3 = m.group(15);
							}
							else
								throw new RuntimeException("Invalid type: " + m.group(13));
						}
					}
				}

				ApplicationClient.checkCommunications(args);

				if (myClient1 == null && myClient2 == null)
					throw new RuntimeException("No server available anymore, exiting...");

				/**/ if (m.group(1).equals("write"))
				{
					System.out.println("**** OK, writting...");

					boolean success = false;
					boolean retried = false;

					while (!success && !retried)
					{
						if (tupleType1 != null && tupleValue1.length() != 0)
						{
							Object tupleObj1 = TupleImplementation.createTupleValue(tupleValue1, tupleType1);

							if (tupleType2 != null && tupleValue2.length() != 0)
							{
								Object tupleObj2 = TupleImplementation.createTupleValue(tupleValue2, tupleType2);

								if (tupleType3 != null && tupleValue3.length() != 0)
								{
									Object tupleObj3 = TupleImplementation.createTupleValue(tupleValue3, tupleType3);
						
									try
									{
										if (myClient1 != null)
										{
											myClient1.write(tupleObj1, ApplicationClient.typeOrNull(tupleType1),
												tupleObj2, ApplicationClient.typeOrNull(tupleType2),
												tupleObj3, ApplicationClient.typeOrNull(tupleType3));
										
											success = true;
										}
									}
									catch (Exception e)
									{
										myClient1 = null;
									}
								
									try
									{
										if (myClient2 != null)
										{
											myClient2.write(tupleObj1, ApplicationClient.typeOrNull(tupleType1),
												tupleObj2, ApplicationClient.typeOrNull(tupleType2),
												tupleObj3, ApplicationClient.typeOrNull(tupleType3));
										
											success = true;
										}
									}
									catch (Exception e)
									{
										myClient2 = null;
									}
								}
								else
								{
									try
									{
										if (myClient1 != null)
										{
											myClient1.write(tupleObj1, ApplicationClient.typeOrNull(tupleType1),
												tupleObj2, ApplicationClient.typeOrNull(tupleType2));
										
											success = true;
										}
									}
									catch (Exception e)
									{
										myClient1 = null;
									}

									try
									{
										if (myClient2 != null)
										{
											myClient2.write(tupleObj1, ApplicationClient.typeOrNull(tupleType1),
												tupleObj2, ApplicationClient.typeOrNull(tupleType2));

											success = true;
										}
									}
									catch (Exception e)
									{
										myClient2 = null;
									}
								}
							}
							else
							{
								try
								{
									if (myClient1 != null)
									{
										myClient1.write(tupleObj1, ApplicationClient.typeOrNull(tupleType1));

										success = true;
									}
								}
								catch (Exception e)
								{
									myClient1 = null;
								}

								try
								{
									if (myClient2 != null)
									{
										myClient2.write(tupleObj1, ApplicationClient.typeOrNull(tupleType1));

										success = true;
									}
								}
								catch (Exception e)
								{
									myClient2 = null;
								}
							}
						}
						else
						{
							throw new NoSuchElementException("Empty request, doing nothing!");
						}

						if (retried)
							break;

						ApplicationClient.checkCommunications(args);
						retried = true;
					}

					if (success)
					{
						System.out.println("****");
						System.out.println("**** WRITTEN SUCCESFULLY!");
						System.out.println("****");
					}
				}
				else if (m.group(1).equals("read") || m.group(1).equals("take"))
				{
					Tuple got = null;
					boolean retried = false;

					if (m.group(1).equals("read"))
					{
						System.out.println("**** GOT 'READ' COMMAND...");

						while (got == null)
						{
							try
							{
								if (myClient1 == null)
									throw new Exception();

								System.out.println("**** Reading (1st)...");

								got = myClient1.read(ApplicationClient.typeOrNull(tupleType1),
									ApplicationClient.typeOrNull(tupleType2),
									ApplicationClient.typeOrNull(tupleType3),
									tupleValue1, tupleValue2, tupleValue3);
							}
							catch (Exception e)
							{
								myClient1 = null;

								try
								{
									System.out.println("**** Reading (2nd)...");

									if (myClient2 == null)
										throw new Exception();

									got = myClient2.read(ApplicationClient.typeOrNull(tupleType1),
										ApplicationClient.typeOrNull(tupleType2),
										ApplicationClient.typeOrNull(tupleType3),
										tupleValue1, tupleValue2, tupleValue3);
								}
								catch (Exception e2)
								{
									myClient2 = null;
								}
							}
							
//							if (got == null && retried)

							if (got == null)
							{
								ApplicationClient.checkCommunications(args);

								if (myClient1 == null && myClient2 == null)
									throw new RuntimeException("Failed to reconnect, exiting...");

//								retried = true;
							}
						}
					}
					else
					{
						System.out.println("**** GOT 'TAKE' COMMAND...");

						while (got == null)
						{
							Client tmpClient = null;

							try
							{
								if (myClient1 != null)
								{
									tmpClient = myClient1;

									System.out.println("**** Taking (1st)...");

									got = myClient1.take(ApplicationClient.typeOrNull(tupleType1),
										ApplicationClient.typeOrNull(tupleType2),
										ApplicationClient.typeOrNull(tupleType3),
										tupleValue1, tupleValue2, tupleValue3);
								}

								if (myClient2 != null)
								{
									tmpClient = myClient2;

									System.out.println("**** Taking (2nd)...");

									got = myClient2.take(ApplicationClient.typeOrNull(tupleType1),
										ApplicationClient.typeOrNull(tupleType2),
										ApplicationClient.typeOrNull(tupleType3),
										tupleValue1, tupleValue2, tupleValue3);
								}
							}
							catch (Exception e)
							{
								if (tmpClient == myClient1)
								{
									myClient1 = null;
									tmpClient = myClient2;
								}
								else
								{
									myClient2 = null;
									tmpClient = myClient1;
								}
							
								if (tmpClient != null)
								{
									try
									{
										System.out.println("**** Taking (retry)...");

										got = tmpClient.read(ApplicationClient.typeOrNull(tupleType1),
											ApplicationClient.typeOrNull(tupleType2),
											ApplicationClient.typeOrNull(tupleType3),
											tupleValue1, tupleValue2, tupleValue3);
									}
									catch (Exception e2)
									{
										if (tmpClient == myClient1)
											myClient1 = null;
										else
											myClient2 = null;
									}
								}
							}
							
							if (got == null && retried)
								throw new RuntimeException("Failed to reconnect, exiting...");

							if (got == null)
							{
								ApplicationClient.checkCommunications(args);
								retried = true;
							}
						}
					}
						
					System.out.println("**** ");
					System.out.println("**** GOT TUPLE<n=" + got.numElements + "> with: ");
					System.out.println("**** ");

					if (got.numElements >= 1)
					{
						System.out.print("****   [1] " + TupleImplementation.anyToString(got.e1, tupleType1));
						System.out.println(" (" + tupleType1 + ")");
					}

					if (got.numElements >= 2)
					{
						System.out.print("****   [2] " + TupleImplementation.anyToString(got.e2, tupleType2));
						System.out.println(" (" + tupleType2 + ")");
					}

					if (got.numElements >= 3)
					{
						System.out.print("****   [3] " + TupleImplementation.anyToString(got.e3, tupleType3));
						System.out.println(" (" + tupleType3 + ")");
					}
					
					System.out.println("****");
				}
				else
				{
					throw new NoSuchElementException("Invalid request!");
				}
			}
			catch (IllegalStateException e)
			{
				System.out.println("**** ERROR matching value, illegal state!");
			}
			catch (IndexOutOfBoundsException e)
			{
				System.out.println("**** ERROR matching value, index out of bounds!");
			}
			catch (IOException e)
			{
				System.out.println("**** ERROR reading input, exiting!");
			}
			catch (NoSuchElementException e)
			{
				System.out.println("**** ERROR: " + e.getMessage());
			}
		}
	}
}
